/**************************************************************************//**
 * @item     CosyOS Kernel
 * @file     taskmge.c
 * @brief    SysTick_Handler and Task_Scheduler
 * @author   迟凯峰
 * @version  V2.3.2
 * @date     2023.05.02
 ******************************************************************************/

#include "syslink.h"

static u32 _DEBUG_HMEM_ usedtime[2];

void SysTick_Handler(void) __STK_ATTRIBUTE__
{
	tTID _SYS_REG_ i;
	/* The SysTick Time Counting */
	#if SYSCFG_STKTIMECOUNT == __ENABLED__
	static tSysTick _DEBUG_HMEM_ tick_temp;
	if(vTaskmgrBinary)
	{
		tick_temp = mSysTick_Counter;
	}
	#endif
	/* The Soft-RTC */
	#if SYSCFG_SOFTRTC == __ENABLED__
	{
		static
		#if SYSCFG_STKCYCLE > 1000000UL / 65536
		u16
		#else
		u32
		#endif
		_SYS_MEM_ counter = 0;
		static u8 _SYS_MEM_ rtc[7] = sInitRealTime;
		static tsVarNode _MALLOC_MEM_ var_node = {vRealTime, rtc, 7};
		if(counter < 1000000UL / SYSCFG_STKCYCLE - 1) counter++;
		else
		{
			counter = 0;
			vEvery.second = true;
			if(rtc[5] < 59) rtc[5]++;
			else
			{
				rtc[5] = 0;
				vEvery.minute = true;
				if(rtc[4] < 59) rtc[4]++;
				else
				{
					rtc[4] = 0;
					vEvery.hour = true;
					if(rtc[3] < 23) rtc[3]++;
					else
					{
						rtc[3] = 0;
						vEvery.day = true;
						if(rtc[6] < 7) rtc[6]++;
						else rtc[6] = 1;
						if(rtc[2] < (rtc[1] == 2 ? vMonth2Date : cMonthDate[rtc[1]])) rtc[2]++;
						else
						{
							rtc[2] = 1;
							vEvery.month = true;
							if(rtc[1] < 12) rtc[1]++;
							else
							{
								rtc[1] = 1;
								vEvery.year = true;
								if(rtc[0] < 99) rtc[0]++;
								else rtc[0] = 0;
							}
						}
					}
				}
			}
		}
		sWRITE_VAR_WRITE(&var_node);
		*(u32 *)(vRealTime + 0) = *(u32 *)(rtc + 0);
		*(u16 *)(vRealTime + 4) = *(u16 *)(rtc + 4);
		*(u8  *)(vRealTime + 6) = *(u8  *)(rtc + 6);
		sWRITE_VAR_WRITE(NULL);
	}
	#endif
	/* The Tick Hook */
	#if SYSCFG_TICKHOOK == __ENABLED__
	tick_hook();
	#endif
	/* The Timing Interrupt-Timer */
	i = OS_TIMINTTOTAL;
	while(i--)
	{
		if(vTIMINT_STMR[i])
		{
			vTIMINT_STMR[i]--;
			if(!vTIMINT_STMR[i])
			{
				if(vTIMINT_TYPE[i])
				{
					sResumeTask_TimInt(i);
				}
				else
				{
					(*vTIMINT_THDL[i]->HOOK)();
				}
				if(vTIMINT_ARLD[i])
				{
					vTIMINT_STMR[i] = vTIMINT_BUFF[i];
				}
			}
		}
	}
	/* The Timing Query-Timer */
	i = OS_TIMQRYTOTAL;
	while(i--)
	{
		if(vTIMQRY_STMR[i] && ~vTIMQRY_STMR[i])
		{
			vTIMQRY_STMR[i]--;
		}
		if(!vTIMQRY_STMR[i])
		{
			if((*vTIMQRY_THDL[i]->EVENT)())
			{
				if(vTIMQRY_TYPE[i])
				{
					sResumeTask_TimQry(i);
				}
				else
				{
					(*vTIMQRY_THDL[i]->HOOK)();
				}
				if(vTIMQRY_ARLD[i])
				{
					vTIMQRY_STMR[i] = vTIMQRY_BUFF[i];
				}
				else
				{
					vTIMQRY_STMR[i] = ~0;
				}
			}
		}
	}
	/* The Delay-Timer */
	i = OS_TASKTOTAL;
	do{
		if(vDELAY_STMR[i])
		{
			vDELAY_STMR[i]--;
		}
	}while(--i);
	/* The SysTick Counting for Time Sharing */
	vTickCounter++;
	/* Safe Run Time */
	if(vTASKING != NULL && vTASKING->TPL && vTASKING->saferuntime)
	{
		vTASKING->counter++;
		if(vTASKING->counter > 1UL * vTASKING->saferuntime * OS_TIMESHARING)
		{
			vTASKING->state = __OVERTIME__;
			vOVERTIME[vTASKING->TID] = true;
			vAlarm.overtime_saferuntime = true;
		}
	}
	if(vTaskmgrBinary)
	{
		/* The used time */
		usedtime[0]++;
		/* The SysTick Time Counting */
		#if SYSCFG_STKTIMECOUNT == __ENABLED__
		mSTK_Counting;
		#endif
	}
	/* Task Scheduling */
	vScheduling_f = true;
	mPSV_Trigger;
	mSTK_END;
}

/* task_node */
#define	node_head       task_node[0]
#define	node_midd       task_node[1]
#define	node_tail       task_node[2]
#define	node_curr       node_buff[0]
#define	node_news       node_buff[1]
#define	node_temp       node_buff[2]
#define	node_last       node_curr->last
#define	node_next       node_curr->next

/* API */
#define	init_taskqueue  node_head = node_midd = node_tail = node_news->last = node_news->next = node_news
#define	init_node_head  node_curr = node_head
#define	init_node_midd  node_curr = node_midd
#define	move_forward    node_curr = node_last
#define	move_backward   node_curr = node_next

#define	node_remove	\
do{	\
	node_last->next = node_next;	\
	node_next->last = node_last;	\
    task_queue_len--;	\
}while(false)

#define forward   0x01
#define voidward  0x00
#define backward  0xFF

#define	insert_forward	\
do{	\
	node_news->next = node_curr;	\
	node_news->last = node_last;	\
	node_last->next = node_news;	\
	node_last = node_news;	\
	switch(insert_direction)	\
	{	\
		case forward:  insert_direction = voidward; node_midd = node_midd->last; break;	\
		case voidward: insert_direction = forward;  break;	\
		case backward: insert_direction = voidward; break;	\
	}	\
}while(false)

#define	insert_backward	\
do{	\
	node_news->last = node_curr;	\
	node_news->next = node_next;	\
	node_next->last = node_news;	\
	node_next = node_news;	\
	if(node_curr == node_tail)	\
	{	\
		node_tail = node_news;	\
	}	\
	switch(insert_direction)	\
	{	\
		case forward:  insert_direction = voidward; break;	\
		case voidward: insert_direction = backward; break;	\
		case backward: insert_direction = voidward; node_midd = node_midd->next; break;	\
	}	\
}while(false)

#define task_state_over	\
do{	\
	if(flag1)	\
	{	\
		node_news = node_curr;	\
		if(vTASKING == NULL || node_curr->TPL != vTASKING->TPL)	\
		{	\
			goto LABLE_SCH;	\
		}	\
		else	\
		{	\
			flag1 = false;	\
			flag2 = true;	\
		}	\
	}	\
}while(false)

void Task_Scheduler(void) __PSV_ATTRIBUTE__
{
	mISV;
	do{
		static u8            _SYS_MEM_ insert_direction = 0;
		static tTaskQueueLen _SYS_MEM_ task_queue_len = 0;
		static tspTaskNode   _SYS_MEM_ task_node[3] = {NULL};
		       tspTaskNode   _SYS_REG_ node_buff[3];
		       tStackSize    _SYS_REG_ stacklen;
		       tSysTick      _SYS_REG_ counter;
		tBIT flag1 = true;
		tBIT flag2 = false;
		/* Scheduler INIT */
		mScheduler_INIT;
		/* Startup Task */
		if(vACTBUF != NULL)
		{
			if(task_queue_len < __TASKQUEUEMAXLEN__)
			{
				#if SYSCFG_TASKCREATEMODE == __STATIC__
				node_news = vACTBUF;
				if(node_news->stacksize < __BASICSTACKSIZE__)
				{
					node_news->state = __STOPPED_TSOF__;
					vFault.overflow_task_stack = true;
				}
				do{
					mTaskStack_INIT;
					if(node_news->TPL0 <= (u8)(SYSCFG_TASKPRIORITY - 1))
					{
						node_news->TPL = node_news->TPL0;
					}
					else
					{
						node_news->TPL = SYSCFG_TASKPRIORITY - 2;
						vAlarm.overflow_task_priority = true;
					}
					node_news->blocktype = 0;
					node_news->ptr = NULL;
					node_news->usedtime[0] = 0;
					node_news->usedtime[1] = 0;
					node_news->stacklen_max = __BASICSTACKSIZE__;
					node_news->counter = 0;
					vDELAY_STMR[node_news->TID] = 0;
				}while(false);
				#elif SYSCFG_TASKCREATEMODE == __BALANCE__
				node_news = vACTBUF;
				if(node_news->stacksize < __BASICSTACKSIZE__)
				{
					node_news->stacksize = __BASICSTACKSIZE__;
					node_news->realloc = true;
					vAlarm.realloc_task_stack = true;
				}
				else
				{
					node_news->realloc = false;
				}
				node_news->BSP = NULL;
				node_news->BSP = (u8 *)__malloc(node_news->stacksize);
				if(node_news->BSP != NULL)
				{
					mTaskStack_INIT;
					if(node_news->TPL0 <= (u8)(SYSCFG_TASKPRIORITY - 1))
					{
						node_news->TPL = node_news->TPL0;
					}
					else
					{
						node_news->TPL = SYSCFG_TASKPRIORITY - 2;
						vAlarm.overflow_task_priority = true;
					}
					node_news->blocktype = 0;
					node_news->ptr = NULL;
					node_news->usedtime[0] = 0;
					node_news->usedtime[1] = 0;
					node_news->stacklen_max = __BASICSTACKSIZE__;
					node_news->counter = 0;
					vDELAY_STMR[node_news->TID] = 0;
				}
				else
				{
					vFault.malloc_fail_task_stack = true;
					goto LABLE_RET;
				}
				#elif SYSCFG_TASKCREATEMODE == __DYNAMIC__
				node_news = NULL;
				node_news = (tspTaskNode)__malloc(sizeof(tsTaskNode));
				if(node_news != NULL)
				{
					node_news->stacksize = vACTBUF->stacksize;
					if(node_news->stacksize < __BASICSTACKSIZE__)
					{
						node_news->stacksize = __BASICSTACKSIZE__;
						node_news->realloc = true;
						vAlarm.realloc_task_stack = true;
					}
					else
					{
						node_news->realloc = false;
					}
					node_news->BSP = NULL;
					node_news->BSP = (u8 *)__malloc(node_news->stacksize);
					if(node_news->BSP != NULL)
					{
						mTaskStack_INIT;
						node_news->TID = vACTBUF->TID;
						if(vACTBUF->TPL0 <= (u8)(SYSCFG_TASKPRIORITY - 1))
						{
							node_news->TPL = vACTBUF->TPL0;
						}
						else
						{
							node_news->TPL = SYSCFG_TASKPRIORITY - 2;
							vAlarm.overflow_task_priority = true;
						}
						node_news->state = vACTBUF->task_node == NULL ? __READY__ : __SUSPENDED__;
						node_news->blocktype = 0;
						node_news->ptr = NULL;
						node_news->NAME = vACTBUF->NAME;
						node_news->usedtime[0] = 0;
						node_news->usedtime[1] = 0;
						node_news->stacklen_max = __BASICSTACKSIZE__;
						node_news->saferuntime = vACTBUF->saferuntime;
						node_news->counter = 0;
						vDELAY_STMR[node_news->TID] = 0;
						vACTBUF->task_node = node_news;
					}
					else
					{
						__free(node_news);
						vFault.malloc_fail_task_stack = true;
						goto LABLE_RET;
					}
				}
				else
				{
					vFault.malloc_fail_task_node = true;
					goto LABLE_RET;
				}
				#endif
				if(!task_queue_len)
				{
					init_taskqueue;
				}
				else
				{
					init_node_midd;
					if(node_curr != node_tail && node_news->TPL <= node_curr->TPL)
					{
						do{
							move_backward;
						}while(node_curr != node_tail && node_news->TPL <= node_curr->TPL);
					}
					else if(node_curr != node_head && node_news->TPL > node_curr->TPL)
					{
						do{
							move_forward;
						}while(node_curr != node_head && node_news->TPL > node_curr->TPL);
					}
					if(node_news->TPL > node_curr->TPL)
					{
						insert_forward;
					}
					else if(node_curr == node_tail && node_curr->TPL == 0)
					{
						insert_forward;
					}
					else
					{
						insert_backward;
					}
				}
				task_queue_len++;
			}
			else
			{
				vAlarm.overflow_task_queue = true;
				goto LABLE_RET;
			}
			vACTBUF = (tspTaskHandle)0;
		}
		LABLE_RET:
		if(vRET_f)
		{
			return;
		}
		/* Task Priority */
		if(vTPLTAIL != NULL)
		{
			node_temp = vTPLTAIL;
			do
			{
				node_news = node_temp;
				node_temp = node_temp->last;
				init_node_midd;
				if(node_curr != node_tail && node_news->TPL <= node_curr->TPL)
				{
					do{
						move_backward;
					}while(node_curr != node_tail && node_news->TPL <= node_curr->TPL);
				}
				else if(node_curr != node_head && node_news->TPL > node_curr->TPL)
				{
					do{
						move_forward;
					}while(node_curr != node_head && node_news->TPL > node_curr->TPL);
				}
				if(node_news->TPL > node_curr->TPL)
				{
					insert_forward;
				}
				else if(node_curr == node_tail && node_curr->TPL == 0)
				{
					insert_forward;
				}
				else
				{
					insert_backward;
				}
			}while(node_temp != NULL);
			vTPLTAIL = NULL;
		}
		/* Task State */
		init_node_head;
		while(true)
		{
			if(flag2)
			{
				if(node_curr->TPL != vTASKING->TPL)
				{
					goto LABLE_SCH;
				}
			}
			switch(node_curr->state)
			{
				case __READY__:	// Ready
				{
					task_state_over;
				}
				break;
				case __BLOCKED__:	// Blocked
				{
					if(node_curr->blocktype == __DELAY__)
					{
						if(!vDELAY_STMR[node_curr->TID])
						{
							node_curr->state = __READY__;
							task_state_over;
						}
					}
					else
					{
						if(node_curr->blocktype & __DELAY__)
						{
							if(!vDELAY_STMR[node_curr->TID])
							{
								node_curr->state = __FLOATING__;
								task_state_over;
								break;
							}
						}
						if(node_curr->blocktype & __BINARY__)
						{
							if(*(bool *)node_curr->ptr)
							{
								node_curr->state = __FLOATING__;
								task_state_over;
							}
						}
						else if(node_curr->blocktype & __MUTEX__)
						{
							if(((tsMut *)node_curr->ptr)->mut)
							{
								node_curr->state = __FLOATING__;
								task_state_over;
							}
						}
						else if(node_curr->blocktype & __SEMAPHORE__)
						{
							if(((tsSem *)node_curr->ptr)->counter)
							{
								node_curr->state = __FLOATING__;
								task_state_over;
							}
						}
						#if SYSCFG_FLAGGROUP == __ENABLED__
						else if(node_curr->blocktype & __FLAGGROUP__)
						{
							if(node_curr->blocktype & __FLAGGROUP_1__)
							{
								if(*(u8 *)node_curr->ptr)
								{
									node_curr->state = __FLOATING__;
									task_state_over;
								}
							}
							else if(node_curr->blocktype & __FLAGGROUP_2__)
							{
								if(*(u16 *)node_curr->ptr)
								{
									node_curr->state = __FLOATING__;
									task_state_over;
								}
							}
							else if(node_curr->blocktype & __FLAGGROUP_4__)
							{
								if(*(u32 *)node_curr->ptr)
								{
									node_curr->state = __FLOATING__;
									task_state_over;
								}
							}
						}
						#endif
						#if SYSCFG_MSGQUEUE == __ENABLED__
						else if(node_curr->blocktype & __RECV_MSG__)
						{
							if(((tspMsgQueue)node_curr->ptr)->counter)
							{
								node_curr->state = __FLOATING__;
								task_state_over;
							}
						}
						#endif
						#if SYSCFG_MAILBOX == __ENABLED__
						else if(node_curr->blocktype & __RECV_MAIL__)
						{
							if(*(bool *)node_curr->ptr)
							{
								node_curr->state = __FLOATING__;
								task_state_over;
							}
						}
						#endif
						#if SYSCFG_DIRMSG == __ENABLED__
						else if(node_curr->blocktype & __RECV_DM__)
						{
							if(*(bool *)node_curr->ptr)
							{
								node_curr->state = __FLOATING__;
								task_state_over;
							}
						}
						#endif
					}
				}
				break;
				case __FLOATING__:	// Floating
				{
					if(node_curr->blocktype & __DELAY__)
					{
						if(!vDELAY_STMR[node_curr->TID])
						{
							task_state_over;
							break;
						}
					}
					if(node_curr->blocktype & __BINARY__)
					{
						if(!*(bool *)node_curr->ptr)
						{
							node_curr->state = __BLOCKED__;
						}
						else
						{
							task_state_over;
						}
					}
					else if(node_curr->blocktype & __MUTEX__)
					{
						if(!((tsMut *)node_curr->ptr)->mut)
						{
							node_curr->state = __BLOCKED__;
						}
						else
						{
							task_state_over;
						}
					}
					else if(node_curr->blocktype & __SEMAPHORE__)
					{
						if(!((tsSem *)node_curr->ptr)->counter)
						{
							node_curr->state = __BLOCKED__;
						}
						else
						{
							task_state_over;
						}
					}
					#if SYSCFG_FLAGGROUP == __ENABLED__
					else if(node_curr->blocktype & __FLAGGROUP__)
					{
						if(node_curr->blocktype & __FLAGGROUP_1__)
						{
							if(!*(u8 *)node_curr->ptr)
							{
								node_curr->state = __BLOCKED__;
							}
							else
							{
								task_state_over;
							}
						}
						else if(node_curr->blocktype & __FLAGGROUP_2__)
						{
							if(!*(u16 *)node_curr->ptr)
							{
								node_curr->state = __BLOCKED__;
							}
							else
							{
								task_state_over;
							}
						}
						else if(node_curr->blocktype & __FLAGGROUP_4__)
						{
							if(!*(u32 *)node_curr->ptr)
							{
								node_curr->state = __BLOCKED__;
							}
							else
							{
								task_state_over;
							}
						}
					}
					#endif
					#if SYSCFG_MSGQUEUE == __ENABLED__
					else if(node_curr->blocktype & __RECV_MSG__)
					{
						if(!((tspMsgQueue)node_curr->ptr)->counter)
						{
							node_curr->state = __BLOCKED__;
						}
						else
						{
							task_state_over;
						}
					}
					#endif
					#if SYSCFG_MAILBOX == __ENABLED__
					else if(node_curr->blocktype & __RECV_MAIL__)
					{
						if(!*(bool *)node_curr->ptr)
						{
							node_curr->state = __BLOCKED__;
						}
						else
						{
							task_state_over;
						}
					}
					#endif
					#if SYSCFG_DIRMSG == __ENABLED__
					else if(node_curr->blocktype & __RECV_DM__)
					{
						if(!*(bool *)node_curr->ptr)
						{
							node_curr->state = __BLOCKED__;
						}
						else
						{
							task_state_over;
						}
					}
					#endif
				}
				break;
				case __DELETED__:	// Delete
				{
					if(node_curr != vTASKING)
					{
						node_remove;
						#if SYSCFG_TASKCREATEMODE != __STATIC__
						__free(node_curr->BSP);
						#endif
						#if SYSCFG_TASKCREATEMODE == __DYNAMIC__
						__free(node_curr);
						#endif
					}
				}
				break;
				case __OVERTIME__:
				{
					if(!vOVERTIME[node_curr->TID]) 
					{
						node_curr->state = __READY__;
						task_state_over;
					}
				}
				break;
			}
			move_backward;
		}
		/* Scheduling */
		LABLE_SCH:
		if(vTASKING != NULL)
		{
			mTaskStack_LEN;
			if(stacklen > vTASKING->stacklen_max)
			{
				vTASKING->stacklen_max = stacklen;
			}
			mEvery_Monitor;
			if(node_news->TPL != vTASKING->TPL);
			else if(vTickCounter >= OS_TIMESHARING || vTASKING->state != __READY__)
			{
				node_curr = vTASKING;
				move_backward;
				while(node_last != node_tail && node_last->TPL == node_curr->TPL)
				{
					if(node_curr->state > __FLOATING__)
					{
						move_backward;
					}
					else
					{
						node_news = node_curr;
						goto LABLE_RDY;
					}
				}
				if(node_news == vTASKING)
				{
					vTickCounter = 0;
					return;
				}
			}
			else
			{
				return;
			}
			LABLE_RDY:
			if(vTASKING->state == __DELETED__)
			{
				node_curr = vTASKING;
				node_remove;
				#if SYSCFG_TASKCREATEMODE != __STATIC__
				__free(node_curr->BSP);
				#endif
				#if SYSCFG_TASKCREATEMODE == __DYNAMIC__
				__free(node_curr);
				#endif
			}
			else if(vTASKING->state < __STOPPED__)
			{
				mPUSH_Monitor;
			}
		}
		/* *** */
		if(node_news->state == __FLOATING__)
		{
			if(node_news->blocktype & __BINARY__)
			{
				if(!*(bool *)node_news->ptr)
				{
					node_news->blocktype = false;
				}
				else if(node_news->blocktype & __BINARY_TAKE__)
				{
					*(bool *)node_news->ptr = false;
				}
			}
			else if(node_news->blocktype & __MUTEX__)
			{
				if(!((tsMut *)node_news->ptr)->mut)
				{
					node_news->blocktype = false;
				}
				else
				{
					((tsMut *)node_news->ptr)->mut = false;
					((tsMut *)node_news->ptr)->node = node_news;
					((tsMut *)node_news->ptr)->TPL = node_news->TPL;
					((tsMut *)node_news->ptr)->tpl = node_news->TPL;
				}
			}
			else if(node_news->blocktype & __SEMAPHORE__)
			{
				if(!((tsSem *)node_news->ptr)->counter)
				{
					node_news->blocktype = false;
				}
				else
				{
					((tsSem *)node_news->ptr)->counter--;
				}
			}
			#if SYSCFG_FLAGGROUP == __ENABLED__
			else if(node_news->blocktype & __FLAGGROUP__)
			{
				if(node_news->blocktype & __FLAGGROUP_1__)
				{
					if(!*(u8 *)node_news->ptr)
					{
						node_news->blocktype = false;
					}
				}
				else if(node_news->blocktype & __FLAGGROUP_2__)
				{
					if(!*(u16 *)node_news->ptr)
					{
						node_news->blocktype = false;
					}
				}
				else if(node_news->blocktype & __FLAGGROUP_4__)
				{
					if(!*(u32 *)node_news->ptr)
					{
						node_news->blocktype = false;
					}
				}
			}
			#endif
			#if SYSCFG_MSGQUEUE == __ENABLED__
			else if(node_news->blocktype & __RECV_MSG__)
			{
				tspMsgNode msg_node;
				tspMsgQueue msg_queue = (tspMsgQueue)node_news->ptr;
				msg_queue->mutex = false;
				if(!msg_queue->counter)
				{
					node_news->ptr = NULL;
				}
				else if(msg_queue->type == __DYNAMIC__)
				{
					sRecvMsg_Dynamic(node_news->ptr);
				}
				else
				{
					sRecvMsg_Static(node_news->ptr);
				}
				msg_queue->mutex = true;
			}
			#endif
			#if SYSCFG_MAILBOX == __ENABLED__
			else if(node_news->blocktype & __RECV_MAIL__)
			{
				if(!*(bool *)node_news->ptr)
				{
					node_news->blocktype = false;
				}
			}
			#endif
			#if SYSCFG_DIRMSG == __ENABLED__
			else if(node_news->blocktype & __RECV_DM__)
			{
				if(!*(bool *)node_news->ptr)
				{
					node_news->blocktype = false;
				}
			}
			#endif
			node_news->state = __READY__;
		}
		vTickCounter = 0;
		if(vTaskmgrBinary)
		{
			counter = mSysTick_Counter;
		}
		/* TaskStack PUSH */
		mTaskStack_PUSH;
		/* TaskStack POP */
		mTaskStack_POP;
	}while(false);
	mPSV_END;
}
